Part 3 of the Pearson Live Training Session “Hands–On Data Visualization with ggplot2” for O’Reilly
library(tidyverse)
theme_set(theme_light(base_size = 18))
data <- readr::read_csv("https://raw.githubusercontent.com/z3tt/hands-on-ggplot2/main/data/crypto_cleaned.csv")
aestheticsOne can use scale_*() to change properties of all the
aesthetic dimensions mapped to the data.
Consequently, there are scale_*() functions for all
aesthetics such as:
scale_x_*() and
scale_y_*()scale_color_*() and
scale_fill_*()scale_size_*() and
scale_radius_*()scale_shape_*() and
scale_linetype_*()scale_alpha_*()The extensions (*) can be filled by e.g.:
continuous(), discrete(),
reverse(), log10(), sqrt(),
date() for positionscontinuous(), discrete(),
manual(), gradient(),
gradient2(), brewer() for colorscontinuous(), discrete(),
manual(), ordinal(), area(),
date() for sizescontinuous(), discrete(),
manual(), ordinal() for shapescontinuous(), discrete(),
manual(), ordinal(), date() for
transparencyscale_*()Scales are directly connected to aesthetics:
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_x_date() +
scale_y_continuous() +
scale_color_discrete()
All scales come with some general and specific arguments to change the appearance:
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_x_date(
expand = c(0, 0), ## general
date_breaks = "4 months", ## date-only
date_labels = "%m/%y", ## date only
name = NULL ## general
) +
scale_y_continuous() +
scale_color_discrete()
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_x_date(
expand = c(0, 0), ## general
date_breaks = "4 months", ## date-only
date_labels = "%m/%y", ## date only
name = NULL ## general
) +
scale_y_continuous(
labels = scales::dollar_format(), ## general
sec.axis = dup_axis(name = NULL), ## axis only
name = "Closing Price" ## general
) +
scale_color_discrete()
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_x_date(
expand = c(0, 0), ## general
date_breaks = "4 months", ## date-only
date_labels = "%m/%y", ## date only
name = NULL ## general
) +
scale_y_continuous(
labels = scales::dollar_format(), ## general
sec.axis = dup_axis(name = NULL), ## axis only
name = "Closing Price" ## general
) +
scale_color_discrete(
type = c("#F0B90B", "#4d4d4e", "#810080", "#00aeff"), ## color only
name = "Cryptocurrency:" ## general
)
scale_color_*()All colors and fills that are mapped to categorical
variables can be manipulated with either
scale_color|fill_discrete() or
scale_color|fill_manual().
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_color_discrete(type = c("#F0B90B", "#4d4d4e","#810080", "#00aeff"))
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_color_manual(values = c("#F0B90B", "#4d4d4e", "#810080", "#00aeff"))
Here, you can overwrite the labels of your legend items—no need to manipulate the data itself!
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_color_manual(
values = c("#F0B90B", "#4d4d4e", "#810080", "#00aeff"),
labels = c("Binance", "Bitcoin", "EOS", "Litecoin"),
name = "Cryptocurrency:"
)
There are a few and well crafted built-in palettes you can use as well (the defaults are pretty bad).
scale_color_viridis_*()ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_color_viridis_d()
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_color_viridis_d(
option = "turbo",
begin = .1,
end = .9,
name = "Cryptocurrency:"
)
The viridis palettes are more suitable for continuous data:
ggplot(data, aes(x = date, y = close, color = yday, group = currency)) +
geom_line() +
scale_color_viridis_c(
option = "turbo",
begin = .1,
end = .9,
name = "Julian Day:"
)
ggplot(data, aes(x = date, y = close, color = open, group = currency)) +
geom_point() +
scale_color_viridis_c(
option = "rocket",
direction = -1,
end = .9,
labels = scales::dollar_format(),
name = "Open Price:"
)
ggplot(data, aes(x = date, y = close, color = open, group = currency)) +
geom_point() +
scale_color_viridis_c(
option = "rocket",
direction = -1,
end = .9,
breaks = seq(50, 300, by = 100),
labels = scales::dollar_format(),
name = "Open Price:"
)
scale_color_brewer_*()Colorbrewer provides color schemes for maps.
RColorBrewer::display.brewer.all()
ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line() +
scale_color_brewer(
palette = "Set2",
name = "Cryptocurrency:"
)
Colorbrewer provides color schemes for maps.
{rnaturalearth}.
economy) as a choropleth
map and use one of the categorical colorbrewer palettes.pop_est).
?scale_fill_brewerscale_fill_gradient()`You can also build your own sequential color palettes:
sf_world <- rnaturalearth::ne_countries(returnclass = "sf")
ggplot(sf_world) +
geom_sf(aes(fill = pop_est), color = NA) +
scale_fill_gradient(low = "grey90", high = "firebrick4") +
coord_sf(crs = "+proj=moll")
scale_fill_gradient2()`You can also build your own diverging color palettes:
ggplot(sf_world) +
geom_sf(aes(fill = as.numeric(factor(income_grp))), color = NA) +
scale_fill_gradient2(low = "firebrick4", high = "darkcyan", mid = "grey90", name = "Income group:") +
coord_sf(crs = "+proj=moll")
ggplot(sf_world) +
geom_sf(aes(fill = as.numeric(factor(income_grp))), color = NA) +
scale_fill_gradient2(low = "firebrick4", high = "darkcyan", mid = "grey90", name = "Income group:",
midpoint = 3) +
coord_sf(crs = "+proj=moll")
RSeveral packages offer predefined palettes, e.g.:
{viridis} for perceptually uniform palettes{scico} for more perceptually uniform palettes{rcartocolor} for map color palettes{ggsci} for scientific journal and sci-fi themed
color{ggthemes} for colors of popular software &
publishers{LaCroixColoR} for vibrant summery colorsCheck the collection
by Emil Hvitfeldt for an extensive list of color palettes available
in R
By the way, you can store ggplots in objects and extend them later:
g <- ggplot(sf_world) +
geom_sf(aes(fill = as.numeric(factor(income_grp))), color = NA) +
scale_fill_gradient2(low = "firebrick4", high = "darkcyan", mid = "grey90", name = "Income group:",
midpoint = 3) +
coord_sf(crs = "+proj=moll")
class(g)
[1] "gg" "ggplot"
guides()The guides() function allows to adjust the appearance of
each legend:
g +
guides(fill = guide_colorbar())
g +
guides(fill = guide_legend())
g +
guides(fill = guide_legend(reverse = TRUE))
g +
guides(fill = guide_legend(reverse = TRUE, keyheight = unit(2, "lines")))
g +
guides(fill = guide_colorbar(barheight = unit(12, "lines"),
barwidth = unit(1, "lines")))
theme()The position of the legend and some other properties can be changed
via theme():
g +
guides(fill = guide_legend()) +
theme(legend.position = "top")
g +
guides(fill = guide_legend()) +
theme(legend.position = "top", legend.key.height = unit(.5, "lines"),
legend.key.width = unit(4, "lines"))
You can change the position of the legend title and labels in
guides():
g +
guides(fill = guide_legend(title.position = "top", title.hjust = .5, label.position = "bottom")) +
theme(legend.position = "top", legend.key.height = unit(.5, "lines"),
legend.key.width = unit(4, "lines"))
theme_*()You have already seen built-in themes in segment 1:
gg <- ggplot(data, aes(x = date, y = close, color = currency)) +
geom_line(size = 1.5)
gg +
theme_grey() +
ggtitle("theme_grey() or theme_gray()")
gg +
theme_classic() +
ggtitle("theme_classic()")
gg +
theme_light() +
ggtitle("theme_light()")
gg +
theme_minimal() +
ggtitle("theme_minimal()")
gg +
theme_dark() +
ggtitle("theme_dark()")
gg +
theme_linedraw() +
ggtitle("theme_linedraw()")
gg +
theme_void() +
ggtitle("theme_void()")
You can either add theme changes to each plot or globally for all plots:
ggplot(data, aes(x = date, y = close)) +
geom_line(aes(color = currency)) +
theme_classic(base_size = 16)
theme_set(theme_classic(base_size = 16))
ggplot(data, aes(x = date, y = close)) +
geom_line(aes(color = currency))
theme_grey
function (base_size = 11, base_family = "", base_line_size = base_size/22,
base_rect_size = base_size/22)
{
half_line <- base_size/2
t <- theme(line = element_line(colour = "black", size = base_line_size,
linetype = 1, lineend = "butt"), rect = element_rect(fill = "white",
colour = "black", size = base_rect_size, linetype = 1),
text = element_text(family = base_family, face = "plain",
colour = "black", size = base_size, lineheight = 0.9,
hjust = 0.5, vjust = 0.5, angle = 0, margin = margin(),
debug = FALSE), axis.line = element_blank(), axis.line.x = NULL,
axis.line.y = NULL, axis.text = element_text(size = rel(0.8),
colour = "grey30"), axis.text.x = element_text(margin = margin(t = 0.8 *
half_line/2), vjust = 1), axis.text.x.top = element_text(margin = margin(b = 0.8 *
half_line/2), vjust = 0), axis.text.y = element_text(margin = margin(r = 0.8 *
half_line/2), hjust = 1), axis.text.y.right = element_text(margin = margin(l = 0.8 *
half_line/2), hjust = 0), axis.ticks = element_line(colour = "grey20"),
axis.ticks.length = unit(half_line/2, "pt"), axis.ticks.length.x = NULL,
axis.ticks.length.x.top = NULL, axis.ticks.length.x.bottom = NULL,
axis.ticks.length.y = NULL, axis.ticks.length.y.left = NULL,
axis.ticks.length.y.right = NULL, axis.title.x = element_text(margin = margin(t = half_line/2),
vjust = 1), axis.title.x.top = element_text(margin = margin(b = half_line/2),
vjust = 0), axis.title.y = element_text(angle = 90,
margin = margin(r = half_line/2), vjust = 1), axis.title.y.right = element_text(angle = -90,
margin = margin(l = half_line/2), vjust = 0), legend.background = element_rect(colour = NA),
legend.spacing = unit(2 * half_line, "pt"), legend.spacing.x = NULL,
legend.spacing.y = NULL, legend.margin = margin(half_line,
half_line, half_line, half_line), legend.key = element_rect(fill = "grey95",
colour = NA), legend.key.size = unit(1.2, "lines"),
legend.key.height = NULL, legend.key.width = NULL, legend.text = element_text(size = rel(0.8)),
legend.text.align = NULL, legend.title = element_text(hjust = 0),
legend.title.align = NULL, legend.position = "right",
legend.direction = NULL, legend.justification = "center",
legend.box = NULL, legend.box.margin = margin(0, 0, 0,
0, "cm"), legend.box.background = element_blank(),
legend.box.spacing = unit(2 * half_line, "pt"), panel.background = element_rect(fill = "grey92",
colour = NA), panel.border = element_blank(), panel.grid = element_line(colour = "white"),
panel.grid.minor = element_line(size = rel(0.5)), panel.spacing = unit(half_line,
"pt"), panel.spacing.x = NULL, panel.spacing.y = NULL,
panel.ontop = FALSE, strip.background = element_rect(fill = "grey85",
colour = NA), strip.text = element_text(colour = "grey10",
size = rel(0.8), margin = margin(0.8 * half_line,
0.8 * half_line, 0.8 * half_line, 0.8 * half_line)),
strip.text.x = NULL, strip.text.y = element_text(angle = -90),
strip.text.y.left = element_text(angle = 90), strip.placement = "inside",
strip.placement.x = NULL, strip.placement.y = NULL, strip.switch.pad.grid = unit(half_line/2,
"pt"), strip.switch.pad.wrap = unit(half_line/2,
"pt"), plot.background = element_rect(colour = "white"),
plot.title = element_text(size = rel(1.2), hjust = 0,
vjust = 1, margin = margin(b = half_line)), plot.title.position = "panel",
plot.subtitle = element_text(hjust = 0, vjust = 1, margin = margin(b = half_line)),
plot.caption = element_text(size = rel(0.8), hjust = 1,
vjust = 1, margin = margin(t = half_line)), plot.caption.position = "panel",
plot.tag = element_text(size = rel(1.2), hjust = 0.5,
vjust = 0.5), plot.tag.position = "topleft", plot.margin = margin(half_line,
half_line, half_line, half_line), complete = TRUE)
ggplot_global$theme_all_null %+replace% t
}
<bytecode: 0x000000002148b988>
<environment: namespace:ggplot2>
theme() ArgumentsThere are many elements you can customize. You can either group them by their type or by their category:
text → all labels, axis text, legend title and
textline → axis lines, ticks, grid linesrect → plot area, panel area, legend and legend keys,
facetsaxis.* → titles, text, ticks, lineslegend.* → background, margin, spacing, keys, text,
title, position, direction, boxpanel.* → background, border, margin, spacing, grid
(major and minor)plot.* → background, title, subtitle, caption, tag,
marginstrip.* → background, placement, textYou can directly alter the appearance by adding theme()
to a ggplot.
element_rect()my_rect_element <- element_rect(
color = "black",
fill = "orange",
size = 2,
linetype = "dotted"
)
g +
theme(plot.background = my_rect_element, legend.background = my_rect_element)
element_line()g +
theme(panel.grid = element_line(
color = "black",
size = 3,
linetype = "dashed",
lineend = "square", # round, butt
arrow = arrow(angle = 30, length = unit(0.25, "inches"))
))
element_text()my_text_element <- element_text(
#family = "Roboto", ## you need {systemfonts} for this
face = "bold", ## plain, italic, bolditalic
size = 24,
color = "firebrick",
lineheight = .7,
angle = 180,
hjust = .5,
vjust = .0,
margin = margin(
10, ## t (top)
0, ## r (right)
30, ## b (bottom)
0 ## l (left)
)
)
g +
ggtitle("My New\nTitle") +
theme(plot.title = my_text_element, legend.text = my_text_element)
theme_grey/theme_gray and one of the other
themes. What’s the difference?Modify theme components on the ggplot reference page
Chapter 19 Themes of the “ggplot2” book by Hadley Wickham et al.
“Creating and Using Custom ggplot2 Themes”, blog post by Thomas Mock
“Themes to Improve Your ggplot Figures” from R for the Rest of Us with a collection of additional themes
“A
{ggplot2} Tutorial for Beautiful Plotting in R”, my
extensive “how to”-tutorial
[1] "2022-06-25 13:59:10 CEST"
Local: main C:/Users/DataVizard/Google Drive/Work/DataViz/Teaching/2022_OReilly_Trainings/hands-on-ggplot2-training
Remote: main @ origin (https://github.com/z3tt/hands-on-ggplot2-training.git)
Head: [1cc6183] 2022-02-18: fix typo
R version 4.1.0 (2021-05-18)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19043)
Matrix products: default
locale:
[1] LC_COLLATE=German_Germany.1252 LC_CTYPE=German_Germany.1252
[3] LC_MONETARY=German_Germany.1252 LC_NUMERIC=C
[5] LC_TIME=German_Germany.1252
system code page: 65001
attached base packages:
[1] stats graphics grDevices utils datasets methods
[7] base
other attached packages:
[1] forcats_0.5.1 stringr_1.4.0 dplyr_1.0.7 purrr_0.3.4
[5] readr_2.0.2 tidyr_1.1.4 tibble_3.1.6 ggplot2_3.3.5
[9] tidyverse_1.3.1
loaded via a namespace (and not attached):
[1] fs_1.5.0 sf_1.0-5 lubridate_1.8.0
[4] bit64_4.0.5 RColorBrewer_1.1-2 httr_1.4.2
[7] tools_4.1.0 backports_1.2.1 bslib_0.3.1
[10] utf8_1.2.2 R6_2.5.1 KernSmooth_2.23-20
[13] DBI_1.1.2 colorspace_2.0-2 withr_2.4.3
[16] sp_1.4-6 tidyselect_1.1.1 downlit_0.4.0
[19] git2r_0.29.0 bit_4.0.4 curl_4.3.2
[22] compiler_4.1.0 textshaping_0.3.6 cli_3.1.0
[25] rvest_1.0.2 xml2_1.3.2 labeling_0.4.2
[28] sass_0.4.0 scales_1.1.1 classInt_0.4-3
[31] proxy_0.4-26 systemfonts_1.0.3 digest_0.6.29
[34] rmarkdown_2.11 pkgconfig_2.0.3 htmltools_0.5.2
[37] dbplyr_2.1.1 fastmap_1.1.0 highr_0.9
[40] rlang_1.0.2 readxl_1.3.1 rstudioapi_0.13
[43] jquerylib_0.1.4 farver_2.1.0 generics_0.1.1
[46] jsonlite_1.7.2 vroom_1.5.5 distill_1.3
[49] magrittr_2.0.1 Rcpp_1.0.7 munsell_0.5.0
[52] fansi_0.5.0 lifecycle_1.0.1 stringi_1.7.5
[55] yaml_2.2.1 grid_4.1.0 parallel_4.1.0
[58] crayon_1.4.2 lattice_0.20-44 haven_2.4.3
[61] hms_1.1.1 knitr_1.39 pillar_1.6.4
[64] reprex_2.0.1 glue_1.4.2 evaluate_0.15
[67] modelr_0.1.8 vctrs_0.3.8 tzdb_0.1.2
[70] cellranger_1.1.0 gtable_0.3.0 assertthat_0.2.1
[73] cachem_1.0.6 xfun_0.31 broom_0.8.0
[76] e1071_1.7-9 rnaturalearth_0.1.0 ragg_1.1.3
[79] class_7.3-19 viridisLite_0.4.0 memoise_2.0.1
[82] units_0.7-2 ellipsis_0.3.2